{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# [LangChain MCP Adapters](https://github.com/langchain-ai/langchain-mcp-adapters)\n",
    "\n",
    "This library provides a lightweight wrapper that makes [Anthropic Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) tools compatible with [LangChain](https://github.com/langchain-ai/langchain) and [LangGraph](https://github.com/langchain-ai/langgraph).\n",
    "\n",
    "## Features\n",
    "\n",
    "- 🛠️ Convert MCP tools into [LangChain tools](https://python.langchain.com/docs/concepts/tools/) that can be used with [LangGraph](https://github.com/langchain-ai/langgraph) agents\n",
    "- 📦 A client implementation that allows you to connect to multiple MCP servers and load tools from them\n",
    "\n",
    "## Quickstart\n",
    "\n",
    "Here is a simple example of using the MCP tools with a LangGraph agent.\n",
    "\n",
    "```bash\n",
    "pip install langchain-mcp-adapters langgraph langchain-openai\n",
    "\n",
    "export OPENAI_API_KEY=<your_api_key>\n",
    "export OPENAI_API_BASE=<your_api_base>\n",
    "```\n",
    "\n",
    "### Server\n",
    "\n",
    "First, let's create an MCP server that can add and multiply numbers.\n",
    "\n",
    "```py\n",
    "# math_server.py\n",
    "from mcp.server.fastmcp import FastMCP\n",
    "\n",
    "mcp = FastMCP(\"Math\")\n",
    "\n",
    "@mcp.tool()\n",
    "def add(a: int, b: int) -> int:\n",
    "    \"\"\"Add two numbers\"\"\"\n",
    "    return a + b\n",
    "\n",
    "@mcp.tool()\n",
    "def multiply(a: int, b: int) -> int:\n",
    "    \"\"\"Multiply two numbers\"\"\"\n",
    "    return a * b\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    mcp.run(transport=\"stdio\")\n",
    "```\n",
    "\n",
    "### Client\n",
    "\n",
    "👇 LangSmith Trace: https://smith.langchain.com/public/31317cdf-2716-4561-8532-370fdd5add35/r\n",
    "\n",
    "> You might notice the tool calling sequence is not correct; this is due to the model's limitations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The result of (3 + 5) x 12 is 96.\n"
     ]
    }
   ],
   "source": [
    "# Create server parameters for stdio connection\n",
    "from mcp import ClientSession, StdioServerParameters\n",
    "from mcp.client.stdio import stdio_client\n",
    "\n",
    "from langchain_mcp_adapters.tools import load_mcp_tools\n",
    "from langgraph.prebuilt import create_react_agent\n",
    "\n",
    "from langchain_openai import ChatOpenAI\n",
    "model = ChatOpenAI(model=\"Qwen/Qwen2.5-7B-Instruct\")\n",
    "\n",
    "server_params = StdioServerParameters(\n",
    "    command=\"python\",\n",
    "    # Make sure to update to the full absolute path to your math_server.py file\n",
    "    args=[\"/Users/haili/workspaces/langgraph-exercise-book/langchain-mcp-adapters/math_server.py\"],\n",
    ")\n",
    "\n",
    "async with stdio_client(server_params) as (read, write):\n",
    "    async with ClientSession(read, write) as session:\n",
    "        # Initialize the connection\n",
    "        await session.initialize()\n",
    "\n",
    "        # Get tools\n",
    "        tools = await load_mcp_tools(session)\n",
    "\n",
    "        # Create and run the agent\n",
    "        agent = create_react_agent(model, tools)\n",
    "        agent_response = await agent.ainvoke({\"messages\": \"what's (3 + 5) x 12?\"})\n",
    "        print(agent_response[\"messages\"][-1].content)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Multiple MCP Servers\n",
    "\n",
    "The library also allows you to connect to multiple MCP servers and load tools from them:\n",
    "\n",
    "### Server\n",
    "\n",
    "```python\n",
    "# math_server.py\n",
    "...\n",
    "\n",
    "# weather_server.py\n",
    "from typing import List\n",
    "from mcp.server.fastmcp import FastMCP\n",
    "\n",
    "mcp = FastMCP(\"Weather\")\n",
    "\n",
    "@mcp.tool()\n",
    "async def get_weather(location: str) -> int:\n",
    "    \"\"\"Get weather for location.\"\"\"\n",
    "    return \"It's always sunny in New York\"\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    mcp.run(transport=\"sse\")\n",
    "```\n",
    "\n",
    "```bash\n",
    "python weather_server.py\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "After running the weather server and invoking the agent, you should see the following output:\n",
    "\n",
    "```bash\n",
    "❯ python ./weather_server.py\n",
    "INFO:     Started server process [73767]\n",
    "INFO:     Waiting for application startup.\n",
    "INFO:     Application startup complete.\n",
    "INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)\n",
    "INFO:     127.0.0.1:65140 - \"GET /sse HTTP/1.1\" 200 OK\n",
    "INFO:     127.0.0.1:65142 - \"POST /messages/?session_id=62ea9f2834d944caa1bfc888a30ffcdd HTTP/1.1\" 202 Accepted\n",
    "INFO:     127.0.0.1:65144 - \"POST /messages/?session_id=62ea9f2834d944caa1bfc888a30ffcdd HTTP/1.1\" 202 Accepted\n",
    "INFO:     127.0.0.1:65146 - \"POST /messages/?session_id=62ea9f2834d944caa1bfc888a30ffcdd HTTP/1.1\" 202 Accepted\n",
    "Processing request of type ListToolsRequest\n",
    "INFO:     127.0.0.1:65154 - \"POST /messages/?session_id=62ea9f2834d944caa1bfc888a30ffcdd HTTP/1.1\" 202 Accepted\n",
    "Processing request of type CallToolRequest\n",
    "```\n",
    "\n",
    "👇 LangSmith Trace (Weather Server): https://smith.langchain.com/public/143a5a8b-8f23-4cdc-bf12-c0471bbe0280/r"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Connecting to SSE endpoint: http://localhost:8000/sse\n",
      "HTTP Request: GET http://localhost:8000/sse \"HTTP/1.1 200 OK\"\n",
      "Received endpoint URL: http://localhost:8000/messages/?session_id=62ea9f2834d944caa1bfc888a30ffcdd\n",
      "Starting post writer with endpoint URL: http://localhost:8000/messages/?session_id=62ea9f2834d944caa1bfc888a30ffcdd\n",
      "HTTP Request: POST http://localhost:8000/messages/?session_id=62ea9f2834d944caa1bfc888a30ffcdd \"HTTP/1.1 202 Accepted\"\n",
      "HTTP Request: POST http://localhost:8000/messages/?session_id=62ea9f2834d944caa1bfc888a30ffcdd \"HTTP/1.1 202 Accepted\"\n",
      "HTTP Request: POST http://localhost:8000/messages/?session_id=62ea9f2834d944caa1bfc888a30ffcdd \"HTTP/1.1 202 Accepted\"\n",
      "HTTP Request: POST https://api.siliconflow.cn/v1/chat/completions \"HTTP/1.1 200 OK\"\n",
      "HTTP Request: POST https://api.siliconflow.cn/v1/chat/completions \"HTTP/1.1 200 OK\"\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The result of (3 + 5) x 12 is 96.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "HTTP Request: POST https://api.siliconflow.cn/v1/chat/completions \"HTTP/1.1 200 OK\"\n",
      "HTTP Request: POST http://localhost:8000/messages/?session_id=62ea9f2834d944caa1bfc888a30ffcdd \"HTTP/1.1 202 Accepted\"\n",
      "HTTP Request: POST https://api.siliconflow.cn/v1/chat/completions \"HTTP/1.1 200 OK\"\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "It seems there was an issue retrieving the exact weather data for NYC. However, the message received suggests it's currently sunny in New York. For the most accurate and up-to-date weather information, please check a reliable weather website or app.\n"
     ]
    }
   ],
   "source": [
    "from langchain_mcp_adapters.client import MultiServerMCPClient\n",
    "from langgraph.prebuilt import create_react_agent\n",
    "\n",
    "from langchain_openai import ChatOpenAI\n",
    "model = ChatOpenAI(model=\"Qwen/Qwen2.5-7B-Instruct\")\n",
    "\n",
    "async with MultiServerMCPClient(\n",
    "    {\n",
    "        \"math\": {\n",
    "            \"command\": \"python\",\n",
    "            # Make sure to update to the full absolute path to your math_server.py file\n",
    "            \"args\": [\"/Users/haili/workspaces/langgraph-exercise-book/langchain-mcp-adapters/math_server.py\"],\n",
    "            \"transport\": \"stdio\",\n",
    "        },\n",
    "        \"weather\": {\n",
    "            # make sure you start your weather server on port 8000\n",
    "            \"url\": \"http://localhost:8000/sse\",\n",
    "            \"transport\": \"sse\",\n",
    "        }\n",
    "    }\n",
    ") as client:\n",
    "    agent = create_react_agent(model, client.get_tools())\n",
    "\n",
    "    math_response = await agent.ainvoke({\"messages\": \"what's (3 + 5) x 12?\"})\n",
    "    print(math_response[\"messages\"][-1].content)\n",
    "\n",
    "    weather_response = await agent.ainvoke({\"messages\": \"what is the weather in nyc?\"})\n",
    "    print(weather_response[\"messages\"][-1].content)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
